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:
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) {
|
||||
|
||||
Reference in new issue
Block a user