feat(report-collaboration): complete RC-BE-015–018 backend, eval suite, and MySQL sync

Ship report rewrite/version APIs, security budget and audit persistence, AgentScope worker wiring, and the 20-task quality eval harness so the frontend can integrate against a stable contract once enabled in config.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
董宏凯andCursor committed 2026-09-06 17:32:15 +08:00
1 parent 814a44f0b9
commit 5e953c9fcb
123 files changed
+26816 -3505

No files matched your search

@@ -3,6 +3,8 @@
实现计划:`docs/agentscope-多智能体报告协作工作台-前端实施计划.md`
(配套后端计划:`docs/agentscope-多智能体报告协作工作台-后端实施计划.md`)。
**进度(2026-09-05):** 本模块页面与契约已完成。后端 `RC-BE-000`~`011` 已通规划前 REST 与 durable SSE;成员自动执行 / 报告改写仍未就绪,联调见后端计划 §0。
## 结构
```text
@@ -48,9 +50,9 @@ src/report-collaboration/
- 服务器快照 + durable SSE 是唯一事实源;`previewPlanId` / 聚焦成员 / 面板宽度均为纯展示状态。
- 节点抽屉只读合同;重跑/纠偏经对话命令(`targetNodeId`/`agentRunId`)。
## Mock 演示(后端未就绪时)
## Mock 演示(执行内核未就绪时)
开启方式任一:
规划前 REST 可走真实后端(需打开 `report_collaboration.enabled`)。成员自动执行、报告改写仍用 mock:
- `localStorage.setItem('deerflow.rc-mock','1')` 后刷新;
- 环境变量 `VITE_RC_MOCK=1`。
@@ -61,6 +63,7 @@ Mock 走与真实后端相同的驱动接口与事件协议(seq 单调、可
## 后端契约冻结说明
`api/types.ts` 按后端计划 §5/§7 编写;快照端点约定 `GET /sessions/{id}` 返回
`SessionSnapshot`(session/messages/plans/run/nodes/agent_runs/commands/report_versions/last_seq)。
后端实现时如有出入,以 OpenAPI 为准同步本文件与两份计划文档。
`api/types.ts` 按后端计划 §5/§7 编写,已与 `app/report_collaboration/contracts/` 对齐。
快照端点约定 `GET /sessions/{id}` 返回 `SessionSnapshot`
(session/messages/plans/run/nodes/agent_runs/commands/report_versions/last_seq)。
`GET /runs/{id}/stream` 已按 `after_seq` / `Last-Event-ID` 重放 durable 事件(`RC-BE-011`)。queued run 的领取/租约恢复已落地(`RC-BE-012`),质量角色内核已落地(`RC-BE-013`)。默认 `worker_enabled=false` 仍不挂后台循环。报告改写 / apply / restore 已由 `RC-BE-015` 挂载。后端 20 项质量评测集见 `RC-BE-018`(确定性打分,不替代上线前真人盲评)。
@@ -253,6 +253,8 @@ export interface ReportRun {
cancel_requested: boolean;
next_event_seq: number;
current_phase?: string | null;
usage?: Record<string, unknown>;
budget_exhausted_reason?: string | null;
created_at: string;
updated_at: string;
}
@@ -416,6 +418,7 @@ export type CollaborationEventType =
| "command.accepted"
| "command.completed"
| "command.failed"
| "command.cancelled"
// 报告与评审
| "report.delta"
| "report.version.created"
@@ -594,6 +597,11 @@ export interface CommandFailedPayload {
error: string;
}
export interface CommandCancelledPayload {
command_id: string;
command?: CollaborationCommand;
}
export interface ReportDeltaPayload {
draft_id?: string | null;
delta: string;
@@ -38,6 +38,7 @@ const KNOWN_EVENT_TYPES = new Set<string>([
"command.accepted",
"command.completed",
"command.failed",
"command.cancelled",
"report.delta",
"report.version.created",
"review.created",
@@ -26,6 +26,7 @@ import type {
CollaborationNodeState,
CollaborationNodeStatus,
CommandAcceptedPayload,
CommandCancelledPayload,
CommandClassifiedPayload,
CommandCompletedPayload,
CommandConfirmationRequiredPayload,
@@ -455,6 +456,12 @@ function applyOne(state: CollaborationState, event: CollaborationEvent): Collabo
if (!command) return state;
return upsertCommand(state, { ...command, status: "failed", error: payload.error });
}
case "command.cancelled": {
const payload = event.data as CommandCancelledPayload;
const command = payload.command ?? state.commands[payload.command_id];
if (!command) return state;
return upsertCommand(state, { ...command, status: "cancelled" });
}
case "report.delta": {
const payload = event.data as ReportDeltaPayload;
const draftId = payload.draft_id ?? "draft";
@@ -21,13 +21,16 @@
--rc-accent: var(--primary, #5c62ff);
--rc-accent-rgb: var(--legacy-primary-rgb, 92 98 255);
--rc-accent-soft: rgb(var(--rc-accent-rgb) / 10%);
--rc-accent-hover: rgb(var(--rc-accent-rgb) / 16%);
--rc-running: #2563eb;
--rc-waiting: #d97706;
--rc-success: #16a34a;
--rc-danger: #dc2626;
--rc-panel-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.04), 0 4px 12px 0 rgba(0, 0, 0, 0.02);
--rc-card-radius: 9px;
--rc-panel-shadow: 0 1px 2px 0 rgba(15, 21, 40, 0.04), 0 4px 14px 0 rgba(15, 21, 40, 0.05);
--rc-panel-shadow-lift: 0 2px 6px 0 rgba(15, 21, 40, 0.06), 0 12px 28px 0 rgba(15, 21, 40, 0.08);
--rc-card-radius: 10px;
--rc-panel-radius: 12px;
--rc-control-radius: 8px;
/* Coze Studio 画布皮肤(节点/连线/端口/点阵),不改交互语义 */
--rc-flow-edge: #abb0f5;
--rc-flow-port: #4d53e8;
@@ -62,10 +65,11 @@
justify-content: space-between;
gap: 8px;
min-height: 56px;
padding: 8px 12px;
padding: 8px 14px;
border-bottom: 1px solid var(--rc-border);
background: var(--rc-bg-panel);
flex-shrink: 0;
box-shadow: 0 1px 0 0 rgb(var(--rc-accent-rgb) / 4%);
}
.rc-header-title {
@@ -75,6 +79,7 @@
text-overflow: ellipsis;
white-space: nowrap;
max-width: 40%;
letter-spacing: 0.01em;
}
.rc-header-meta {
@@ -83,6 +88,19 @@
white-space: nowrap;
}
/* 顶栏内返回按钮:圆形 icon-only,弱化按钮观感 */
.rc-header > .rc-btn:not(.rc-btn-primary):not(.rc-btn-danger) {
border-color: transparent;
background: transparent;
color: var(--rc-text-secondary);
}
.rc-header > .rc-btn:not(.rc-btn-primary):not(.rc-btn-danger):hover:not(:disabled) {
border-color: var(--rc-border);
background: var(--rc-bg-secondary);
color: var(--rc-text-primary);
}
/* ── 主体两栏 ─────────────────────────────────────────────────────────── */
.rc-body {
@@ -153,19 +171,26 @@
.rc-status-bar {
display: flex;
align-items: center;
gap: 16px;
height: 28px;
padding: 0 12px;
font-size: 12px;
gap: 18px;
height: 30px;
padding: 0 14px;
font-size: 11.5px;
color: var(--rc-text-secondary);
background: var(--rc-bg-secondary);
background: var(--rc-bg-panel);
border-top: 1px solid var(--rc-border);
flex-shrink: 0;
overflow: hidden;
white-space: nowrap;
}
.rc-status-bar > span {
display: inline-flex;
align-items: center;
font-variant-numeric: tabular-nums;
}
.rc-status-dot {
position: relative;
display: inline-block;
width: 8px;
height: 8px;
@@ -173,19 +198,31 @@
margin-right: 6px;
vertical-align: middle;
background: var(--rc-text-secondary);
box-shadow: 0 0 0 2px var(--rc-bg-panel);
}
.rc-status-dot[data-status="live"] {
background: var(--rc-success);
box-shadow:
0 0 0 2px var(--rc-bg-panel),
0 0 0 4px rgb(22 163 74 / 18%);
}
.rc-status-dot[data-status="connecting"],
.rc-status-dot[data-status="reconnecting"] {
background: var(--rc-waiting);
box-shadow: 0 0 0 2px var(--rc-bg-panel);
animation: rc-status-pulse 1.4s ease-in-out infinite;
}
.rc-status-dot[data-status="error"] {
background: var(--rc-danger);
box-shadow: 0 0 0 2px var(--rc-bg-panel);
}
@keyframes rc-status-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.45; }
}
/* ── 按钮 / 标签 / 卡片通用 ──────────────────────────────────────────── */
@@ -193,62 +230,96 @@
.rc-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
min-height: 30px;
padding: 5px 10px;
padding: 5px 12px;
font-size: 12px;
font-weight: 600;
border-radius: 7px;
border-radius: var(--rc-control-radius);
border: 1px solid var(--rc-border-strong);
background: var(--rc-bg-panel);
color: var(--rc-text-primary);
cursor: pointer;
transition: background 0.16s ease, border-color 0.16s ease, opacity 0.16s ease;
transition:
background 0.18s ease,
border-color 0.18s ease,
color 0.18s ease,
box-shadow 0.18s ease,
transform 0.18s ease,
opacity 0.18s ease;
white-space: nowrap;
user-select: none;
}
.rc-btn:hover:not(:disabled) {
border-color: var(--rc-accent);
color: var(--rc-accent);
background: var(--rc-accent-soft);
}
.rc-btn:active:not(:disabled) {
transform: translateY(0.5px);
}
.rc-btn:focus-visible {
outline: none;
box-shadow: 0 0 0 3px rgb(var(--rc-accent-rgb) / 22%);
}
.rc-btn:disabled {
opacity: 0.6;
opacity: 0.55;
cursor: not-allowed;
}
.rc-btn-primary {
background: var(--rc-accent);
border-color: var(--rc-accent);
color: var(--rc-bg-panel);
color: #fff;
box-shadow: 0 1px 0 0 rgb(var(--rc-accent-rgb) / 35%), 0 4px 10px -4px rgb(var(--rc-accent-rgb) / 35%);
}
.rc-btn-primary:hover:not(:disabled) {
color: var(--rc-bg-panel);
filter: brightness(1.05);
color: #fff;
background: var(--rc-accent);
filter: brightness(1.08);
border-color: var(--rc-accent);
}
.rc-btn-primary:disabled {
background: var(--rc-accent);
border-color: var(--rc-accent);
opacity: 0.55;
filter: none;
}
.rc-btn-danger {
background: var(--rc-danger);
border-color: var(--rc-danger);
color: #fff;
box-shadow: 0 1px 0 0 rgb(220 38 38 / 30%);
}
.rc-btn-danger:hover:not(:disabled) {
color: #fff;
background: var(--rc-danger);
filter: brightness(1.05);
border-color: var(--rc-danger);
}
.rc-chip {
display: inline-flex;
align-items: center;
padding: 1px 7px;
gap: 3px;
padding: 1px 8px;
font-size: 11px;
font-weight: 500;
line-height: 18px;
border-radius: 999px;
background: var(--rc-accent-soft);
color: var(--rc-accent);
white-space: nowrap;
letter-spacing: 0.01em;
}
.rc-badge-num {
@@ -263,6 +334,7 @@
color: #fff;
font-size: 10px;
font-weight: 600;
line-height: 1;
}
/* ── 方案卡 ───────────────────────────────────────────────────────────── */
@@ -270,24 +342,47 @@
.rc-plan-card {
display: flex;
flex-direction: column;
gap: 5px;
padding: 10px;
gap: 8px;
padding: 12px 14px;
border-radius: var(--rc-card-radius);
border: 1px solid var(--rc-border);
background: var(--rc-bg-panel);
cursor: pointer;
transition: border-color 0.16s ease, box-shadow 0.16s ease;
transition:
border-color 0.18s ease,
box-shadow 0.18s ease,
background 0.18s ease,
transform 0.18s ease;
text-align: left;
width: 100%;
position: relative;
overflow: hidden;
}
.rc-plan-card::before {
content: "";
position: absolute;
inset: 0 auto 0 0;
width: 3px;
background: transparent;
transition: background 0.18s ease;
}
.rc-plan-card:hover {
border-color: rgb(var(--rc-accent-rgb) / 45%);
border-color: rgb(var(--rc-accent-rgb) / 50%);
background: rgb(var(--rc-accent-rgb) / 3%);
}
.rc-plan-card-selected {
border-color: var(--rc-accent);
box-shadow: 0 0 0 2px rgb(var(--rc-accent-rgb) / 14%);
background: rgb(var(--rc-accent-rgb) / 5%);
box-shadow:
0 0 0 1px var(--rc-accent),
0 6px 18px -8px rgb(var(--rc-accent-rgb) / 28%);
}
.rc-plan-card-selected::before {
background: var(--rc-accent);
}
.rc-plan-card-disabled {
@@ -295,6 +390,11 @@
cursor: not-allowed;
}
.rc-plan-card-disabled:hover {
border-color: var(--rc-border);
background: var(--rc-bg-panel);
}
/* ── 并行成员头像条(对齐会商第二步)─────────────────────────────────── */
.rc-agent-tabs {
@@ -931,19 +1031,33 @@
.rc-composer {
border-top: 1px solid var(--rc-border);
padding: 8px;
padding: 10px 12px 8px;
background: var(--rc-bg-panel);
flex-shrink: 0;
position: relative;
}
.rc-composer::before {
content: "";
position: absolute;
inset: 0 0 auto 0;
height: 1px;
background: linear-gradient(
90deg,
transparent,
rgb(var(--rc-accent-rgb) / 22%),
transparent
);
}
.rc-composer-hint {
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
font-size: 11.5px;
color: var(--rc-text-secondary);
padding: 2px 4px 6px;
min-height: 22px;
padding: 0 4px 6px;
min-height: 20px;
}
.rc-composer-hint[data-tone="warning"] {
@@ -959,36 +1073,118 @@
align-items: center;
gap: 4px;
font-size: 11px;
padding: 1px 7px;
padding: 2px 8px;
border-radius: 999px;
background: var(--rc-accent-soft);
color: var(--rc-accent);
border: 1px solid rgb(var(--rc-accent-rgb) / 18%);
}
/* Composer 内的输入框 / 发送按钮 —— 默认 inline-style 已经写好,这里覆盖为更现代的样式 */
.rc-composer textarea {
min-height: 92px !important;
border-radius: var(--rc-control-radius) !important;
padding: 10px 44px 12px 12px !important;
font-size: 13px !important;
line-height: 1.55 !important;
background: var(--rc-bg-page) !important;
box-shadow: inset 0 1px 2px 0 rgb(15 21 40 / 4%);
transition: border-color 0.18s ease, box-shadow 0.18s ease, background 0.18s ease;
}
.rc-composer textarea:focus {
border-color: var(--rc-accent) !important;
background: var(--rc-bg-panel) !important;
box-shadow:
inset 0 1px 2px 0 rgb(15 21 40 / 4%),
0 0 0 3px rgb(var(--rc-accent-rgb) / 16%);
}
.rc-composer textarea::placeholder {
color: var(--rc-text-secondary);
opacity: 0.7;
}
.rc-composer textarea + button {
width: 36px !important;
height: 36px !important;
border-radius: var(--rc-control-radius) !important;
right: 6px !important;
bottom: 6px !important;
box-shadow: 0 2px 6px 0 rgb(var(--rc-accent-rgb) / 32%);
transition: background 0.18s ease, transform 0.18s ease, box-shadow 0.18s ease, filter 0.18s ease;
}
.rc-composer textarea + button:hover:not(:disabled) {
filter: brightness(1.08);
box-shadow: 0 4px 10px 0 rgb(var(--rc-accent-rgb) / 38%);
}
.rc-composer textarea + button:active:not(:disabled) {
transform: scale(0.96);
}
/* 快捷键提示行 */
.rc-composer .text-\[10px\] {
font-size: 10.5px !important;
}
/* ── 命令确认卡 / 干预卡 ──────────────────────────────────────────────── */
.rc-intervention-card {
position: relative;
border: 1px solid rgba(217, 119, 6, 0.45);
border-radius: var(--rc-card-radius);
background: rgba(217, 119, 6, 0.05);
padding: 10px 12px;
background: linear-gradient(
180deg,
rgba(217, 119, 6, 0.07),
rgba(217, 119, 6, 0.03)
);
padding: 12px 14px;
display: flex;
flex-direction: column;
gap: 8px;
gap: 10px;
width: 100%;
overflow: hidden;
}
.rc-intervention-card::before {
content: "";
position: absolute;
inset: 0 auto 0 0;
width: 3px;
background: var(--rc-waiting);
}
.rc-intervention-card > .flex.items-start {
margin-left: 2px;
}
/* ── 报告卡 ───────────────────────────────────────────────────────────── */
.rc-report-card {
position: relative;
border: 1px solid rgba(22, 163, 74, 0.4);
border-radius: var(--rc-card-radius);
background: rgba(22, 163, 74, 0.04);
padding: 10px 12px;
background: linear-gradient(
180deg,
rgba(22, 163, 74, 0.08),
rgba(22, 163, 74, 0.03)
);
padding: 12px 14px;
display: flex;
flex-direction: column;
gap: 8px;
gap: 10px;
width: 100%;
overflow: hidden;
}
.rc-report-card::before {
content: "";
position: absolute;
inset: 0 auto 0 0;
width: 3px;
background: var(--rc-success);
}
/* ── 历史侧栏 Tab ─────────────────────────────────────────────────────── */
@@ -996,54 +1192,79 @@
.rc-tabs {
display: flex;
gap: 4px;
padding: 8px 8px 4px;
padding: 10px 10px 6px;
position: relative;
}
.rc-tab {
position: relative;
flex: 1;
height: 32px;
border-radius: 8px;
border: none;
background: transparent;
font-size: 12px;
font-weight: 500;
color: var(--rc-text-secondary);
cursor: pointer;
transition: background 0.16s ease, color 0.16s ease;
transition: background 0.18s ease, color 0.18s ease;
}
.rc-tab:hover:not([data-active="true"]) {
background: var(--rc-bg-secondary);
color: var(--rc-text-primary);
}
.rc-tab[data-active="true"] {
background: var(--rc-accent-soft);
color: var(--rc-accent);
font-weight: 600;
box-shadow: inset 0 0 0 1px rgb(var(--rc-accent-rgb) / 18%);
}
/* 报告预览 Dialog 内左侧版本列表用的 .rc-tab:左对齐、紧凑 */
.rc-root .rc-canvas-panel ~ * .rc-tab,
.rc-root .fixed .rc-tab {
text-align: left;
}
/* ── 窄屏单面板切换 ───────────────────────────────────────────────────── */
.rc-narrow-switch {
display: flex;
display: inline-flex;
gap: 2px;
padding: 4px;
padding: 3px;
border-radius: 8px;
border: 1px solid var(--rc-border);
background: var(--rc-bg-secondary);
background: var(--rc-bg-panel);
box-shadow: var(--rc-panel-shadow);
}
.rc-narrow-switch button {
height: 26px;
padding: 0 10px;
padding: 0 12px;
border: none;
border-radius: 6px;
background: transparent;
font-size: 12px;
font-weight: 500;
color: var(--rc-text-secondary);
cursor: pointer;
display: inline-flex;
align-items: center;
transition: background 0.16s ease, color 0.16s ease, box-shadow 0.16s ease;
}
.rc-narrow-switch button:hover:not([data-active="true"]) {
color: var(--rc-text-primary);
background: var(--rc-bg-secondary);
}
.rc-narrow-switch button[data-active="true"] {
background: var(--rc-bg-panel);
background: var(--rc-accent-soft);
color: var(--rc-accent);
font-weight: 600;
box-shadow: var(--rc-panel-shadow);
box-shadow: inset 0 0 0 1px rgb(var(--rc-accent-rgb) / 18%);
}
/* ── 空态 / 加载 ──────────────────────────────────────────────────────── */
@@ -1053,12 +1274,29 @@
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
gap: 12px;
height: 100%;
color: var(--rc-text-secondary);
font-size: 13px;
line-height: 1.6;
text-align: center;
padding: 24px;
padding: 32px 24px;
}
.rc-empty > svg {
width: 44px;
height: 44px;
padding: 10px;
border-radius: 14px;
background: var(--rc-accent-soft);
color: var(--rc-accent);
opacity: 0.85;
box-shadow: inset 0 0 0 1px rgb(var(--rc-accent-rgb) / 14%);
}
.rc-empty > p {
max-width: 360px;
margin: 0;
}
.rc-spin {
@@ -1071,6 +1309,203 @@
}
}
/* ── 历史会话侧栏(SessionHistoryPanel:使用 Tailwind utilities,这里在
* .rc-root 作用域内覆写一部分表现以与页面其他控件统一观感) ──────────── */
.rc-root .flex.items-center.justify-between.px-3.py-2 {
padding: 10px 12px 6px;
}
.rc-root .flex.items-center.justify-between.px-3.py-2 > span {
font-size: 11.5px;
font-weight: 500;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--rc-text-secondary);
opacity: 0.85;
}
/* 会话条目:圆角、间距、悬浮反馈;active 用左侧 3px 主色条 */
.rc-root .group.flex.items-center.gap-2.rounded-lg {
padding: 8px 10px;
margin: 1px 4px;
border-radius: 8px;
position: relative;
transition: background 0.16s ease, box-shadow 0.16s ease, transform 0.16s ease;
}
.rc-root .group.flex.items-center.gap-2.rounded-lg::before {
content: "";
position: absolute;
left: -4px;
top: 8px;
bottom: 8px;
width: 3px;
border-radius: 0 3px 3px 0;
background: transparent;
transition: background 0.16s ease;
}
.rc-root .group.flex.items-center.gap-2.rounded-lg.bg-\[var\(--rc-accent-soft\)\] {
background: var(--rc-accent-soft) !important;
box-shadow: inset 0 0 0 1px rgb(var(--rc-accent-rgb) / 18%);
}
.rc-root .group.flex.items-center.gap-2.rounded-lg.bg-\[var\(--rc-accent-soft\)\]::before {
background: var(--rc-accent);
}
.rc-root .group.flex.items-center.gap-2.rounded-lg:hover:not(.bg-\[var\(--rc-accent-soft\)\]) {
background: var(--rc-bg-secondary) !important;
}
/* 会话标题文字 */
.rc-root .group .truncate.text-\[12px\].font-medium {
font-size: 12.5px !important;
letter-spacing: 0.005em;
}
/* 会话操作按钮(重命名 / 删除) */
.rc-root .group .rounded.p-1 {
width: 26px;
height: 26px;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--rc-text-secondary);
transition: background 0.14s ease, color 0.14s ease;
}
.rc-root .group .rounded.p-1:hover {
background: var(--rc-bg-panel);
color: var(--rc-text-primary);
}
.rc-root .group .rounded.p-1:hover svg[style*="--rc-danger"] {
color: var(--rc-danger);
}
/* ── 报告预览 Dialog 头/版本列表(在 .rc-root 作用域内统一观感) ────────── */
.rc-root .fixed.inset-0 > div > header {
background: var(--rc-bg-panel);
padding: 14px 18px;
}
.rc-root .fixed.inset-0 nav {
background: var(--rc-bg-panel);
}
.rc-root .fixed.inset-0 nav .rc-tab {
height: auto;
padding: 6px 10px;
margin: 1px 0;
border-radius: 6px;
text-align: left;
}
.rc-root .fixed.inset-0 nav .rc-tab:hover:not([data-active="true"]) {
background: var(--rc-bg-secondary);
color: var(--rc-text-primary);
}
.rc-root .fixed.inset-0 nav .rc-tab[data-active="true"] {
background: var(--rc-accent-soft);
color: var(--rc-accent);
font-weight: 600;
box-shadow: inset 0 0 0 1px rgb(var(--rc-accent-rgb) / 18%);
}
/* 报告 Markdown 内容:加点行距方便阅读 */
.rc-report-preview {
line-height: 1.75;
font-size: 14px;
}
.rc-report-preview h1,
.rc-report-preview h2,
.rc-report-preview h3 {
margin-top: 1.2em;
margin-bottom: 0.5em;
font-weight: 600;
}
.rc-report-preview p {
margin: 0.6em 0;
}
.rc-report-preview pre {
border-radius: 8px;
padding: 12px 14px;
background: var(--rc-bg-secondary);
border: 1px solid var(--rc-border);
}
.rc-report-preview code {
font-family: ui-monospace, "JetBrains Mono", Menlo, Consolas, monospace;
font-size: 12.5px;
}
/* ── 抽屉 section hover 反馈 ──────────────────────────────────────────── */
.rc-drawer-section-toggle:hover {
color: var(--rc-accent);
}
.rc-drawer-section-toggle:hover .rc-drawer-chevron {
color: var(--rc-accent);
}
.rc-drawer-list li {
padding: 3px 0;
}
/* 抽屉 footer 主按钮撑满 */
.rc-drawer-footer .rc-btn-primary {
font-size: 13px;
min-height: 34px;
}
/* ── 并行成员头像条:稍加 padding 让节奏更舒服 ─────────────────────────── */
.rc-agent-tabs {
padding: 10px 14px;
background: linear-gradient(
180deg,
var(--rc-bg-panel),
rgb(var(--rc-accent-rgb) / 1.5%)
);
}
.rc-agent-avatar {
transition: box-shadow 0.18s ease, transform 0.18s ease, opacity 0.18s ease;
}
.rc-agent-avatar:hover {
transform: translateY(-1px);
box-shadow: 0 4px 10px -2px rgb(15 21 40 / 14%), 0 0 0 2px var(--rc-border);
}
.rc-agent-avatar[data-active="true"] {
box-shadow: 0 0 0 2px var(--rc-accent), 0 4px 10px -2px rgb(var(--rc-accent-rgb) / 30%);
}
.rc-agent-avatar[data-active="true"]:hover {
transform: translateY(-1px);
box-shadow: 0 0 0 2px var(--rc-accent), 0 6px 14px -2px rgb(var(--rc-accent-rgb) / 35%);
}
.rc-agent-strip-meta {
font-size: 11px;
letter-spacing: 0.02em;
color: var(--rc-text-secondary);
}
.rc-agent-strip-meta > span:first-child {
font-weight: 600;
color: var(--rc-text-primary);
}
/* ── 无障碍:减少动效 ─────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {